package com.small.sentinel.slots.flow;

import com.small.common.core.util.StringUtil;
import com.small.sentinel.Context;
import com.small.sentinel.node.DefaultNode;
import com.small.sentinel.node.Node;
import com.small.sentinel.slotchain.ResourceWrapper;
import com.small.sentinel.slots.SentinelResource;
import com.small.sentinel.slots.block.BlockException;
import com.small.sentinel.slots.block.RuleConstant;
import com.small.sentinel.slots.clusterBuilder.ClusterBuilderSlot;

import java.util.Collection;
import java.util.function.Function;

public class FlowRuleChecker {

    public void checkFlow(Function<String, Collection<FlowRule>> ruleProvider, SentinelResource resource) throws BlockException {
        ResourceWrapper resourceWrapper = resource.getResourceWrapper();

        if (ruleProvider == null || resourceWrapper == null) {
            return;
        }
        Collection<FlowRule> rules = ruleProvider.apply(resourceWrapper.getName());

        if (rules != null) {
            for (FlowRule rule : rules) {
                if (!canPassCheck(rule, resource.getContext(), resource.getDefaultNode(), resource.getCount())) {
                    throw new FlowException(rule.getLimitApp(), rule);
                }
            }
        }
    }

    public boolean canPassCheck(FlowRule rule, Context context, DefaultNode node, int acquireCount) {
        String limitApp = rule.getLimitApp();
        if (limitApp == null) {
            return true;
        }
        return passLocalCheck(rule, context, node, acquireCount);
    }

    private static boolean passLocalCheck(FlowRule rule, Context context, DefaultNode node, int acquireCount) {
        Node selectedNode = selectNodeByRequesterAndStrategy(rule, context, node);
        if (selectedNode == null) {
            return true;
        }

        return rule.getRater().canPass(selectedNode, acquireCount);
    }


    /**
     * 选择节点
     *
     * @param rule
     * @param context
     * @param node
     * @return
     */
    static Node selectNodeByRequesterAndStrategy(FlowRule rule, Context context, DefaultNode node) {
        String limitApp = rule.getLimitApp();
        int strategy = rule.getStrategy();

        if (RuleConstant.LIMIT_APP_DEFAULT.equals(limitApp)) {
            if (strategy == RuleConstant.STRATEGY_DIRECT) {
                return node.getClusterNode();
            }

            return selectReferenceNode(rule, context, node);
        }
        return null;
    }

    static Node selectReferenceNode(FlowRule rule, Context context, DefaultNode node) {
        String refResource = rule.getRefResource();
        int strategy = rule.getStrategy();

        if (StringUtil.isEmpty(refResource)) {
            return null;
        }

        /**
         * 关联模式
         */
        if (strategy == RuleConstant.STRATEGY_RELATE) {
            return ClusterBuilderSlot.getClusterNode(refResource);
        }

        /**
         * 链路模式
         */
        if (strategy == RuleConstant.STRATEGY_CHAIN) {
            if (!refResource.equals(context.getName())) {
                return null;
            }
            return node;
        }
        return null;
    }
}
